package com.example.remoteassistance;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.hardware.display.DisplayManager;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.projection.MediaProjection;
import android.util.Log;
import android.view.Surface;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.android.material.tabs.TabLayout;

import java.io.IOException;
import java.nio.ByteBuffer;

//视频编码器
public class VideoEncoder implements Runnable {

    private static final String TAG = "VideoEncoder";

    private static final long TIME_OUT = 10000;
    private MediaCodec mMediaCodec;
    private boolean isEncoding;

    public VideoEncoder(MediaProjection mediaProjection, int width, int height) {
        //设置编码格式 H264
        MediaFormat format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, width, height);
        format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
        format.setInteger(MediaFormat.KEY_BIT_RATE, width * height );
        format.setInteger(MediaFormat.KEY_FRAME_RATE, 20);
        format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
        try {
            mMediaCodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
        } catch (IOException e) {
            e.printStackTrace();
        }
        mMediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
        Surface surface = mMediaCodec.createInputSurface();
        //编码录频数据
        mediaProjection.createVirtualDisplay("screen", width, height, 1, DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC, surface, null, null);
        mMediaCodec.start();
    }

    //启动编码
    public void start() {
        new Thread(this).start();
        isEncoding = true;
    }

    //停止编码
    public void stop() {
        isEncoding = false;
    }

    //销毁
    public void destroy() {
        if (mMediaCodec != null) {
            mMediaCodec.release();
            if (mMediaCodec == null) {
                Log.d(TAG, "mMediaCodec destroy");
            }
        }
    }

    @Override
    public void run() {
        MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
        while (isEncoding) {
            try {
                int outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, TIME_OUT);
                if (outputBufferIndex >= 0) {
                    ByteBuffer byteBuffer = mMediaCodec.getOutputBuffer(outputBufferIndex);
                    encodeData(byteBuffer, bufferInfo);
                    mMediaCodec.releaseOutputBuffer(outputBufferIndex, false);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        Log.d(TAG, "encode thread is ending");
    }

    private static final int NAL_SLICE = 1;
    private static final int NAL_SLICE_DPA = 2;
    private static final int NAL_SLICE_DPB = 3;
    private static final int NAL_SLICE_DPC = 4;
    private static final int NAL_SLICE_IDR = 5;
    private static final int NAL_SEI = 6;
    private static final int NAL_SPS = 7;
    private static final int NAL_PPS = 8;
    private static final int NAL_AUD = 9;
    private static final int NAL_FILLER = 12;
    private byte[] sps_pps_buf;

    private void encodeData(ByteBuffer byteBuffer, MediaCodec.BufferInfo bufferInfo) {
        int offSet = 4;
        if (byteBuffer.get(2) == 0x01) {
            offSet = 3;
        }
        int type = (byteBuffer.get(offSet)) & 0x1F;
        if (type == NAL_SPS) {
            sps_pps_buf = new byte[bufferInfo.size];
            byteBuffer.get(sps_pps_buf);
        } else if (type == NAL_SLICE) {
            final byte[] bytes = new byte[bufferInfo.size];
            byteBuffer.get(bytes);
            if (listener != null) {
                listener.onEncodeData(bytes);
            }
        } else if (type == NAL_SLICE_IDR) {
            final byte[] bytes = new byte[bufferInfo.size];
            byteBuffer.get(bytes);
            byte[] newBytes = new byte[sps_pps_buf.length + bytes.length];
            System.arraycopy(sps_pps_buf, 0, newBytes, 0, sps_pps_buf.length);
            System.arraycopy(bytes, 0, newBytes, sps_pps_buf.length, bytes.length);
            if (listener != null) {
                listener.onEncodeData(newBytes);
            }
        }
    }

    //编码侦听接口
    private EncoderListener listener;
    public void setListener(EncoderListener listener) {
        this.listener = listener;
    }
    public interface EncoderListener {
        void onEncodeData(byte[] bytes);
    }
}


